import * as cc from 'cc';
const PI180 = Math.PI / 180;
const PI360 = Math.PI / 360;
Object.defineProperties(cc.Node.prototype, {
    x: {
        get: function (): number { return this._lpos.x; },
        set: function (val: number) {
            this._lpos.x = val;
            this.invalidateChildren(cc.TransformBit.POSITION);
        }
    },
    y: {
        get: function (): number { return this._lpos.y; },
        set: function (val: number) {
            this._lpos.y = val;
            this.invalidateChildren(cc.TransformBit.POSITION);
        }
    },
    z: {
        get: function (): number { return this._lpos.z; },
        set: function (val: number) {
            this._lpos.z = val;
            this.invalidateChildren(cc.TransformBit.POSITION);
        }
    },
    angleX: {
        get: function (): number { return this._euler.x; },
        set: function (val: number) {
            this._euler.x = val;
            val *= PI360;
            this._lrot.x = Math.sin(val);
            this._lrot.w = Math.cos(val);
            this.invalidateChildren(cc.TransformBit.ROTATION);
        }
    },
    angleY: {
        get: function (): number { return this._euler.y; },
        set: function (val: number) {
            this._euler.y = val;
            val *= PI360;
            this._lrot.y = Math.sin(val);
            this._lrot.w = Math.cos(val);
            this.invalidateChildren(cc.TransformBit.ROTATION);
        }
    },
    angleZ: {
        get: function (): number { return this._euler.z; },
        set: function (val: number) {
            this._euler.z = val;
            val *= PI360;
            this._lrot.z = Math.sin(val);
            this._lrot.w = Math.cos(val);
            this.invalidateChildren(cc.TransformBit.ROTATION);
        }
    },
    scaleX: {
        get: function (): number { return this._lscale.x; },
        set: function (val: number) {
            this._lscale.x = val;
            this.invalidateChildren(cc.TransformBit.SCALE);
        }
    },
    scaleY: {
        get: function (): number { return this._lscale.y; },
        set: function (val: number) {
            this._lscale.y = val;
            this.invalidateChildren(cc.TransformBit.SCALE);
        }
    },
    scaleZ: {
        get: function (): number { return this._lscale.z; },
        set: function (val: number) {
            this._lscale.z = val;
            this.invalidateChildren(cc.TransformBit.SCALE);
        }
    },
    widthUT: {
        get: function (): number {
            let ut = this._uiProps._uiTransformComp ??= this.addComponent(cc.UITransform);
            return ut._contentSize.width;
        },
        set: function (val: number) {
            let ut = this._uiProps._uiTransformComp ??= this.addComponent(cc.UITransform);
            if (ut._contentSize.width === val) return;
            ut._contentSize.width = val;
            ut._markRenderDataDirty();
        }
    },
    heightUT: {
        get: function (): number {
            let ut = this._uiProps._uiTransformComp ??= this.addComponent(cc.UITransform);
            return ut._contentSize.height;
        },
        set: function (val: number) {
            let ut = this._uiProps._uiTransformComp ??= this.addComponent(cc.UITransform);
            if (ut._contentSize.height === val) return;
            ut._contentSize.height = val;
            ut._markRenderDataDirty();
        }
    },
    anchorXUT: {
        get: function (): number {
            let ut = this._uiProps._uiTransformComp ??= this.addComponent(cc.UITransform);
            return ut._anchorPoint.x;
        },
        set: function (val: number) {
            let ut = this._uiProps._uiTransformComp ??= this.addComponent(cc.UITransform);
            if (ut._anchorPoint.x === val) return;
            ut._anchorPoint.x = val;
            ut._markRenderDataDirty();
        }
    },
    anchorYUT: {
        get: function (): number {
            let ut = this._uiProps._uiTransformComp ??= this.addComponent(cc.UITransform);
            return ut._anchorPoint.y;
        },
        set: function (val: number) {
            let ut = this._uiProps._uiTransformComp ??= this.addComponent(cc.UITransform);
            if (ut._anchorPoint.y === val) return;
            ut._anchorPoint.y = val;
            ut._markRenderDataDirty();
        }
    },
    color: {
        get: function (): cc.Color {
            let ur = this._uiProps._uiComp ??= this.addComponent(cc.UIRenderer);
            return ur._color;
        },
        set: function (val: cc.Color) {
            let ur = this._uiProps._uiComp ??= this.addComponent(cc.UIRenderer);
            if (ur._color.equals(val)) return;
            ur._color._val = ((val.a << 24) >>> 0) + (val.b << 16) + (val.g << 8) + (val.r | 0);
            ur._updateColor();
        }
    },
    findChildren: {
        value: function (name: string, isStartWith: boolean = false): cc.Node[] {
            let ret = [];
            let find = isStartWith ? (node: cc.Node) => {
                for (let i = 0, len = node.children.length, childrens = node.children; i < len; ++i) {
                    let children = childrens[i];
                    children.name.startsWith(name) && ret.push(children);
                    find(children);
                }
            } : (node: cc.Node) => {
                for (let i = 0, len = node.children.length, childrens = node.children; i < len; ++i) {
                    let children = childrens[i];
                    children.name === name && ret.push(children);
                    find(children);
                }
            }
            find(this);
            return ret;
        }
    },
    setAnchor: {
        value: function (point: cc.Vec2 | number, y?: number) {
            let ut = this._uiProps._uiTransformComp ?? this.addComponent(cc.UITransform);
            let anchor = ut._anchorPoint;
            if (typeof point === 'object') {
                if (point.x === anchor.x && point.y === anchor.y) return;
            } else {
                if (point === anchor.x && y === anchor.y) return;
                point = new cc.Vec2(point, y);
            }
            let radian = this.angle * PI180;
            let cos = Math.cos(radian);
            let sin = Math.sin(radian);
            let dx = ut._contentSize.width * this._lscale.x * (point.x - anchor.x);
            let dy = ut._contentSize.height * this._lscale.y * (point.y - anchor.y);
            this._lpos.x = this._lpos.x + dx * cos - dy * sin;
            this._lpos.y = this._lpos.y + dx * sin + dy * cos;
            this.invalidateChildren(cc.TransformBit.POSITION);
            ut._anchorPoint = point;
            this.emit(cc.NodeEventType.ANCHOR_CHANGED, point);
            ut._markRenderDataDirty();
        }
    },
    setColor: {
        value: function (time: number, color?: cc.Color) {
            let tweenColor = (tag: any, time: number, color: cc.Color): cc.Tween<unknown> => {
                return cc.tween(tag).to(time, {
                    color: {
                        value: color,
                        progress: (start: number, end: number, current: number, t: number) => {
                            let sr = start & 0x000000ff;
                            let sg = (start & 0x0000ff00) >> 8;
                            let sb = (start & 0x00ff0000) >> 16;
                            let sa = (start & 0xff000000) >>> 24;
                            let er = end & 0x000000ff;
                            let eg = (end & 0x0000ff00) >> 8;
                            let eb = (end & 0x00ff0000) >> 16;
                            let ea = (start & 0xff000000) >>> 24;
                            let r = sr + (er - sr) * t;
                            let g = sg + (eg - sg) * t;
                            let b = sb + (eb - sb) * t;
                            let a = sa + (ea - sa) * t;
                            return ((a << 24) >>> 0) + (b << 16) + (g << 8) + r;
                        }
                    }
                });
            }
            let ur = this._uiProps._uiComp;
            if (ur) {
                this['colorTween']?.stop();
                if (color === undefined) {
                    if (this['defaultColor'] !== undefined) {
                        this['colorTween'] = tweenColor(ur, time, this['defaultColor']).call(() => { delete this['defaultColor']; delete this['colorTween']; }).start();
                    }
                } else {
                    let defaultColor = this['defaultColor'] ??= ur.color.clone();
                    let changeColor = cc.color(defaultColor.r * color.r / 255, defaultColor.g * color.g / 255, defaultColor.b * color.b / 255, defaultColor.a * color.a / 255);
                    time === 0 ? ur.color = changeColor : this['colorTween'] = tweenColor(ur, time, changeColor).start();
                }
            }
            for (let children = this.children, i = children.length - 1; i > -1; children[i--].setColor(time, color));
        }
    },
    shake: {
        value: function (repeat: number, amplitude?: cc.Vec3, frequency?: number) {
            if (this['amplitude']) {
                this._lpos.subtract(this['amplitude']);
                this.invalidateChildren(cc.TransformBit.POSITION);
                clearInterval(this['shakeHandle']);
            }
            if (repeat === 0) {
                delete this['shakeHandle'];
                delete this['amplitude'];
                delete this['frequency'];
                return;
            }
            repeat = ~~repeat;
            if (amplitude) {
                this['amplitude'] = amplitude;
            } else if (this['amplitude']) {
                this['amplitude'].negative();
            } else return;
            this['frequency'] = frequency !== undefined ? Math.max(frequency, 0.01) : this['frequency'] ?? 0.05;
            this._lpos.add(this['amplitude']);
            this.invalidateChildren(cc.TransformBit.POSITION);
            let step = cc.v3();
            if (repeat > 0) {
                repeat = Math.max(repeat - 1, 1);
                step = cc.v3(-this['amplitude'].x / repeat, -this['amplitude'].y / repeat, -this['amplitude'].z / repeat);
            }
            this['shakeHandle'] = setInterval(() => {
                this._lpos.subtract(this['amplitude']);
                this.invalidateChildren(cc.TransformBit.POSITION);
                if (--repeat === 0) {
                    clearInterval(this['shakeHandle']);
                    delete this['shakeHandle'];
                    delete this['amplitude'];
                    delete this['frequency'];
                    return;
                }
                step.negative();
                this['amplitude'].negative().add(step);
                this._lpos.add(this['amplitude']);
                this.invalidateChildren(cc.TransformBit.POSITION);
            }, this['frequency'] * 1000);
        }
    },
    destroyRealtime: {
        value: function () {
            if (!cc.isValid(this, true)) return;
            clearInterval(this['shakeHandle']);
            this.destroy();
            this.removeFromParent();
        }
    },
    destroyAllChildrenRealtime: {
        value: function () {
            if (!cc.isValid(this, true)) return;
            for (let children = this.children, i = children.length - 1; i > -1; children[i--].destroyRealtime());
        }
    },
});